package com.deep.bilibili.ffmpeg;

import com.deep.bilibili.config.FfmpegConfig;
import com.deep.bilibili.config.LiveConfig;
import com.deep.bilibili.util.LiveUtil;
import com.deep.bilibili.util.UploadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;


@Slf4j
@Component
public class FfmpegUtil {

    @Autowired
    FfmpegConfig ffmpegConfig;

    @Autowired
    LiveConfig liveConfig;


    public static FfmpegConfig staticFfmpegConfig = null;
    public static LiveConfig staticLiveConfig = null;


    public static BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(200000);

    //    public static ExecutorService executorService = Executors.newFixedThreadPool(5);
    public static ThreadPoolExecutor executors = new ThreadPoolExecutor(5,
            100,
            100000,
            TimeUnit.DAYS,
            workQueue);

    @PostConstruct
    public void init() {
        staticFfmpegConfig = ffmpegConfig;
        staticLiveConfig = liveConfig;
    }

    /**
     * 获取视频封面
     *
     * @param inputPath
     * @param coverPath
     * @return
     * @throws FFmpegException
     */
    public static Boolean cover(String inputPath, String coverPath) throws FFmpegException {

        if (!UploadUtil.checkFile(inputPath)) {
            throw new FFmpegException("文件格式不合法");
        }

        List command = getCoverCommand(inputPath, coverPath);
        log.info("command  = {}", command);
        if (null != command && command.size() > 0) {
            return process(command);
        }
        return false;
    }

    /**
     * rtmp推流
     *
     * @param inputPath
     * @param streamName
     * @return
     * @throws FFmpegException
     */
    public static Boolean rtmp(String inputPath, String streamName) throws FFmpegException {

        if (!UploadUtil.checkFile(inputPath)) {
            throw new FFmpegException("文件格式不合法");
        }

        List command = getRtmpCommand(inputPath, streamName);
        log.info("command  = {}", command);
        if (null != command && command.size() > 0) {
            return processRtmp(command);
        }
        return false;
    }

    public static Boolean flv(String inputPath, String streamName) throws FFmpegException {

        if (!UploadUtil.checkFile(inputPath)) {
            throw new FFmpegException("文件格式不合法");
        }

        List command = getFlvCommand(inputPath, streamName);
        log.info("command  = {}", command);
        if (null != command && command.size() > 0) {
            return processFlv(command);
        }
        return false;
    }

    public static Boolean hls(String inputPath, String streamName) throws FFmpegException {

        if (!UploadUtil.checkFile(inputPath)) {
            throw new FFmpegException("文件格式不合法");
        }

        List command = getHlsCommand(inputPath, streamName);
        log.info("command  = {}", command);
        if (null != command && command.size() > 0) {
            return processHls(command);
        }
        return false;
    }

    /**
     * 视频转码
     *
     * @param inputPath
     * @param outputPath
     * @return
     * @throws FFmpegException
     */
    public static Boolean convert(String inputPath, String outputPath) throws FFmpegException {

        if (!UploadUtil.checkFile(inputPath)) {
            throw new FFmpegException("文件格式不合法");
        }

        List command = getConvertCommand(inputPath, outputPath);
        if (null != command && command.size() > 0) {
            return process(command);
        }
        return false;
    }


    public static boolean processRtmp(List command) throws FFmpegException {
        log.info("command live = {}", command);
        log.info("command live = {}", StringUtils.joinWith(" ", command));
        try {
            if (null == command || command.size() == 0)
                return false;
//            executors.execute(new LiveStream(command));
            executors.execute(new RtmpStream(command));

//            Thread thread1 = new LiveStream(command);
//            thread1.start();
//            thread1.setDaemon(true);

//            new LiveStream(command).start();
//            new PushStream(command).start();

            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed", e);
        }
    }


    public static boolean processFlv(List command) throws FFmpegException {
        log.info("command live = {}", command);
        log.info("command live = {}", StringUtils.joinWith(" ", command));
        try {
            if (null == command || command.size() == 0)
                return false;
//            executors.execute(new LiveStream(command));
            executors.execute(new RtmpStream(command));

//            Thread thread1 = new LiveStream(command);
//            thread1.start();
//            thread1.setDaemon(true);

//            new LiveStream(command).start();
//            new PushStream(command).start();

            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed", e);
        }
    }


    public static boolean processHls(List command) throws FFmpegException {
        log.info("processHls live = {}", command);
        log.info("processHls live = {}", StringUtils.joinWith(" ", command));
        try {
            if (null == command || command.size() == 0)
                return false;
             executors.execute(new HlsStream(command));

            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed", e);
        }
    }

    public static boolean process(List command) throws FFmpegException {

        try {

            if (null == command || command.size() == 0)
                return false;
            Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();

            new PrintStream(videoProcess.getErrorStream()).start();

            new PrintStream(videoProcess.getInputStream()).start();

            int exitCode = videoProcess.waitFor();

            if (exitCode == 1) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed", e);
        }

    }

    public static List getConvertCommand(String inputFilePath, String outputPath) throws FFmpegException {
        int type = UploadUtil.checkContentType(inputFilePath);

        return getConvertCommand(type, inputFilePath, outputPath);
    }

    /**
     * 文件转码
     *
     * @param type
     * @param inputFilePath
     * @param outputPath
     * @return
     * @throws FFmpegException
     */
    public static List getConvertCommand(int type, String inputFilePath, String outputPath) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");
        outputPath = StringUtils.replace(outputPath, "\\", "/");


        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();
        if (type == 1) {
            command.add(ffmpegPath);
            command.add("-i");
//            command.add("'" + inputFilePath + "'");
            command.add(inputFilePath);
            command.add("-c:v");
            command.add("libx264");
            command.add("-x264opts");
            command.add("force-cfr=1");
            command.add("-c:a");
            command.add("mp2");
            command.add("-b:a");
            command.add("256k");
            command.add("-vsync");
            command.add("cfr");
            command.add("-f");
            command.add("mpegts");
//            command.add("'" + outputPath + "'");
            command.add(outputPath);
        } else if (type == 0) {
            command.add(ffmpegPath);
            command.add("-i");
//            command.add("'" + inputFilePath + "'");
            command.add(inputFilePath);
            command.add("-c:v");
            command.add("libx264");
            command.add("-x264opts");
            command.add("force-cfr=1");
            command.add("-vsync");
            command.add("cfr");
            command.add("-vf");
            command.add("idet,yadif=deint=interlaced");
            command.add("-filter_complex");
            command.add("aresample=async=1000");
            command.add("-c:a");
            command.add("libmp3lame");
            command.add("-b:a");
            command.add("192k");
            command.add("-pix_fmt");
            command.add("yuv420p");
            command.add("-f");
            command.add("mpegts");
//            command.add("'" + outputPath + "'");
            command.add(outputPath);
        } else {
            throw new FFmpegException("不支持当前上传的文件格式");
        }
        return command;
    }


    /**
     * @param inputFilePath
     * @param outputPath
     * @return
     * @throws FFmpegException
     */
    public static List getCoverCommand(String inputFilePath, String outputPath) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");

        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();

        command.add(ffmpegPath);
        command.add("-i");
        command.add(inputFilePath);
        command.add("-y");
        command.add("-f");
        command.add("mjpeg");
        command.add("-ss");
        command.add("0.1");
        command.add("-t");
        command.add("0.001");
        command.add("-s");
        command.add("675*432");
        command.add(outputPath);

        return command;
    }


    public static List getRtmpCommand(String inputFilePath, String streamName) throws FFmpegException {
        String fileExt = "flv";
        return getRtmpCommand(inputFilePath, fileExt, streamName);
    }

    public static List getRtmpCommand(String inputFilePath, String fileExt, String streamName) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");

        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();

        String liveUrl = LiveUtil.getRtmpUrl(streamName);

        command.add(ffmpegPath);
        command.add("-re");
        command.add("-i");
        command.add(inputFilePath);
        // 仅推流视频
//        command.add("-vcodec");
        command.add("-c");
        command.add("copy");
//        command.add("-an");
        command.add("-f");
        command.add(fileExt);
        command.add("-y");
        // 推流地址
        command.add(liveUrl);

        return command;
    }


    public static List getFlvCommand(String inputFilePath, String streamName) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");

        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();

        String liveUrl = LiveUtil.getRtmpUrl(streamName);

        command.add(ffmpegPath);
        command.add("-re");
        command.add("-i");
        command.add(inputFilePath);
        // 仅推流视频
//        command.add("-vcodec");
        command.add("-c");
        command.add("copy");
        command.add("-y");
        // 推流地址
        command.add(liveUrl);

        return command;
    }

    public static List getHlsCommand(String inputFilePath,  String streamName) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");

        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();
        String prefix = staticLiveConfig.getHls().get("prefix");
        String path = staticLiveConfig.getHls().get("path");
        String outputPath = path + "/" + prefix + "/" + streamName;
        UploadUtil.isChartPathExist(outputPath);

        command.add(ffmpegPath);
        command.add("-i");
        command.add(inputFilePath);
        // 仅推流视频
        command.add("-profile:v");
        command.add("baseline");
        command.add("-level");
        command.add("3.0");
        command.add("-s");
        command.add("640x360");
        command.add("-start_number");
        command.add("0");
        command.add("-hls_time");
        command.add("10");
        command.add("-hls_list_size");
        command.add("0");
        command.add("-f");
        command.add("hls");
        // 推流地址
        String m3u8Path = outputPath +  "/" + streamName + ".m3u8";
        log.info("m3u8 path = {}", m3u8Path);
        command.add(m3u8Path);

        return command;
    }

    public static List getRtcCommand(String inputFilePath, String streamName) throws FFmpegException {
        inputFilePath = StringUtils.replace(inputFilePath, "\\", "/");

        List command = new ArrayList();
        String ffmpegPath = staticFfmpegConfig.getFfmpegPath();

        String liveUrl = LiveUtil.getRtmpUrl(streamName);

        command.add(ffmpegPath);
        command.add("-re");
        command.add("-i");
        command.add(inputFilePath);
        // 仅推流视频
//        command.add("-vcodec");
        command.add("-c");
        command.add("copy");
        command.add("-y");
        // 推流地址
        command.add(liveUrl);

        return command;
    }

}

